<template>
  <EditorLayout :flow-graph="flowGraph">
    <div :class="containerClass" ref="wrapperRef">
      <div class="liteflow-editor__graph" ref="graphRef"></div>
      <div class="liteflow-editor__minimap" ref="miniMapRef"></div>
      <Breadcrumb v-if="flowGraph" />
      <ContextMenu v-if="flowGraph" />
      <ContextPad v-if="flowGraph" />
      <slot />
    </div>
  </EditorLayout>
</template>

<script>
import { defineComponent, ref, reactive, computed, onMounted, onBeforeUnmount, provide } from 'vue';
import { createFlowGraph } from './flow/createFlowGraph.js';
import { setGraph, setGraphWrapper } from '../core/state/graph.js';
import { history, setModel, getModel, ELBuilder, MIN_ZOOM, forceLayout } from '../core/index.js';
import { GRAPH_SYMBOL } from './context.js';
import Breadcrumb from './Breadcrumb.vue';
import ContextMenu from './ContextMenu.vue';
import ContextPad from './ContextPad.vue';
import EditorLayout from './EditorLayout.vue';
import { registerVueShapes } from '../core/nodes/register-vue.js';
import { NodeRenderer } from '../core/nodes/NodeRenderer.js';

export default defineComponent({
  name: 'LiteFlowEditor',
  props: {
    className: {
      type: String,
      default: '',
    },
  },
  components: {
    Breadcrumb,
    ContextMenu,
    ContextPad,
    EditorLayout,
  },
  setup(props, { emit }) {
    const wrapperRef = ref(null);
    const graphRef = ref(null);
    const miniMapRef = ref(null);
    const flowGraph = ref(null);
    // 使用非响应式变量存储 graph 实例，避免 Vue 响应式系统影响事件监听器
    let graphInstance = null;
    const nodeRenderer = new NodeRenderer();
    const contextMenuInfo = reactive({
      x: 0,
      y: 0,
      scene: 'blank',
      visible: false,
    });

    const graphDisposers = [];

    const bindGraphEvents = () => {
      if (!graphInstance) return;

      const showContextMenu = (info) => {
        graphInstance?.lockScroller();
        Object.assign(contextMenuInfo, info, { visible: true });
      };
      const hideContextMenu = () => {
        graphInstance?.unlockScroller();
        contextMenuInfo.visible = false;
      };
      const showContextPad = (info) => {
        graphInstance?.lockScroller();
        Object.assign(contextPadInfo, info, { visible: true });
      };
      const hideContextPad = () => {
        graphInstance?.unlockScroller();
        contextPadInfo.visible = false;
      };
      const handleModelChange = () => {
        if (graphInstance) {
          const model = getModel();
          if (model) {
            const modelJSON = model.toCells();
            graphInstance.lockScroller();
            graphInstance.startBatch('update');
            graphInstance.resetCells(modelJSON);
            forceLayout(graphInstance);
            graphInstance.stopBatch('update');
            graphInstance.unlockScroller();
            graphInstance.trigger('model:changed');
          }
        }
      };

      graphInstance.on('graph:showContextMenu', showContextMenu);
      graphInstance.on('graph:hideContextMenu', hideContextMenu);
      graphInstance.on('graph:showContextPad', showContextPad);
      graphInstance.on('graph:hideContextPad', hideContextPad);
      graphInstance.on('model:change', handleModelChange);

      // TODO: 节点渲染器暂时禁用，先让节点基本显示正常
      // 后续再添加 Vue 组件渲染

      graphDisposers.push(() => graphInstance?.off('graph:showContextMenu', showContextMenu));
      graphDisposers.push(() => graphInstance?.off('graph:hideContextMenu', hideContextMenu));
      graphDisposers.push(() => graphInstance?.off('graph:showContextPad', showContextPad));
      graphDisposers.push(() => graphInstance?.off('graph:hideContextPad', hideContextPad));
      graphDisposers.push(() => graphInstance?.off('model:change', handleModelChange));
    };

    const cleanupGraphEvents = () => {
      graphDisposers.splice(0).forEach((dispose) => dispose());
    };
    const contextPadInfo = reactive({
      x: 0,
      y: 0,
      scene: 'append',
      edge: null,
      node: null,
      visible: false,
    });

    const currentEditor = {
      getGraphInstance() {
        return flowGraph.value;
      },
      toJSON() {
        return getModel()?.toJSON() || {};
      },
      fromJSON(data) {
        const model = ELBuilder.build(data || {});
        setModel(model);
        history.cleanHistory();
        flowGraph.value?.zoomToFit({ minScale: MIN_ZOOM, maxScale: 1 });
      },
    };

    provide(GRAPH_SYMBOL, {
      flowGraph,
      contextMenuInfo,
      contextPadInfo,
      currentEditor,
    });

    const handleResize = () => {
      if (flowGraph.value && wrapperRef.value) {
        const width = wrapperRef.value.clientWidth;
        const height = wrapperRef.value.clientHeight;
        flowGraph.value.resize(width, height);
      }
    };

    onMounted(() => {
      registerVueShapes();
      if (graphRef.value && miniMapRef.value) {
        const instance = createFlowGraph(graphRef.value, miniMapRef.value);
        graphInstance = instance; // 保存到非响应式变量
        flowGraph.value = instance; // 仍然保存到 ref 供其他组件使用
        setGraph(instance);
        setGraphWrapper(wrapperRef.value);
        history.init(instance);
        const initialModel = ELBuilder.build([]);
        setModel(initialModel);
        history.push(initialModel, { silent: true });
        emit('ready', instance);
        bindGraphEvents();
        window.addEventListener('resize', handleResize);
      }
    });

    onBeforeUnmount(() => {
      window.removeEventListener('resize', handleResize);
      cleanupGraphEvents();
      nodeRenderer.destroy();
      if (flowGraph.value) {
        flowGraph.value.dispose();
        flowGraph.value = null;
      }
    });

    const containerClass = computed(() => {
      return ['liteflow-editor__container', props.className].filter(Boolean).join(' ');
    });

    return {
      wrapperRef,
      graphRef,
      miniMapRef,
      containerClass,
      flowGraph,
    };
  },
});
</script>

<style lang="less" scoped>
.liteflow-editor__container {
  position: relative;
  width: 100%;
  height: 100%;
  background: #f5f7fb;
}

.liteflow-editor__graph {
  width: 100%;
  height: 100%;
}

.liteflow-editor__minimap {
  position: absolute;
  right: 16px;
  bottom: 16px;
  width: 150px;
  height: 150px;
  border-radius: 4px;
  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.08);
  background: #fff;
}
</style>

